//
// 嗨~ o(*￣▽￣*)ブ
// Created by Mokou on 2020/7/15.
//

#import "FileSelector.h"
#import "Util.h"
#import "GlobalData.h"
#import "FileCopy.h"
#import "EncryptUtil.h"
#import <MobileCoreServices/MobileCoreServices.h>
#import <AVFoundation/AVFoundation.h>
#import <pthread.h>

int maxSelectNum = 10;
typedef void (^copyCB)(NSDictionary *resDict);

@implementation FileSelector {
    NSMutableArray *mediaTypes;
    BOOL allowPhoto;
    BOOL allowVideo;
    FlutterResult result;
    BOOL inSelect;

    // 素材文件相关路径
    NSString *fileDirAbsolute;
    NSString *fileDirRelative;

    // 升级包文件相关路径
    NSString *upgradeFileDir;
    NSString *upgradeFileDir_cache;
}

+ (FileSelector *)shareInstance {
    static dispatch_once_t onceToken;
    static FileSelector *instance;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

- (instancetype)init {
    self = super.init;
    inSelect = NO;
    NSString *createLocalFileDirRes = [self createDirInDocuments:localFileDir];
    fileDirAbsolute = createLocalFileDirRes;
    fileDirRelative = [@"Documents" stringByAppendingPathComponent:localFileDir];

    upgradeFileDir = [self createDirInDocuments:localUpgradeFileDir];
    upgradeFileDir_cache = [self createDirInDocuments:localUpgradeFileFir_cache];
    return self;
}

- (void)resFlutter:(NSArray *)pathArr {
    if (!pathArr || !pathArr.count) {
        result(nil);
    } else {
        result(pathArr);
    }
}

- (void)setType:(int)type result:(FlutterResult)flutterResult {
    if (inSelect) return;

    result = flutterResult;
    mediaTypes = NSMutableArray.new;
    allowPhoto = NO;
    allowVideo = NO;

    if ((type & ImagePickerType_photo) >> 0) {
        [mediaTypes addObject:(NSString *) kUTTypeImage];
        allowPhoto = YES;
    }

    if ((type & ImagePickerType_video) >> 1) {
        [mediaTypes addObject:(NSString *) kUTTypeMovie];
        allowVideo = YES;
    }
}

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *, id> *)info {
    NSString *mediaType = info[UIImagePickerControllerMediaType];

    NSURL *file_url;
    NSString *url_str;

    NSString *method = @"";
    if ([mediaType isEqualToString:(NSString *) kUTTypeImage]) {
        if (@available(iOS 11.0, *)) {
            file_url = info[UIImagePickerControllerImageURL];
        } else {
            file_url = info[UIImagePickerControllerOriginalImage];
        }
        method = fcn_selectPhoto;
    } else if ([mediaType isEqualToString:(NSString *) kUTTypeMovie]) {
        file_url = info[UIImagePickerControllerMediaURL];
        method = fcn_selectVideo;
    }

    BOOL unsupportFormat = !file_url;
    
    if (!unsupportFormat) {
        url_str = file_url.path;
        // 复制文件
        [self copyFile:url_str cb:^(NSDictionary *copy_res) {
            ElecMaterial *material = ElecMaterial.new;
            material.content = copy_res[@"relativePath"];
            material.absolutePath = copy_res[@"absolutePath"];
            material.md5 = copy_res[@"md5"];
            [[GlobalData shareInstance].db tempMedia_insert:material];

            NSLog(@"------%@  res:%@", method, material.content);
            [self resFlutter:@[material.absolutePath]];
        }];
    }
    [picker dismissViewControllerAnimated:YES completion:^(){
        self->inSelect = NO;
        if (unsupportFormat) {
            [Util toast:@"文件格式不支持"];
            NSLog(@"------%@  res:%@", method, @"nil");
            [self resFlutter:nil];
        }
    }];
}

- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
    [picker dismissViewControllerAnimated:YES completion:^(){
        self->inSelect = NO;
        NSLog(@"------%@/%@  res:%@", fcn_selectPhoto,fcn_selectPhoto, @"nil");
        [self resFlutter:nil];
    }];
}

// 原生UIImagePickerController 进行单个视频、图片的选择
- (void)select {
    if (!mediaTypes.count || inSelect) return;
    inSelect = YES;

    UIImagePickerController *picker = [[UIImagePickerController alloc] init];
    picker.delegate = self;
    picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    picker.mediaTypes = mediaTypes;
    picker.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
    picker.videoQuality = UIImagePickerControllerQualityTypeHigh;
    picker.allowsEditing = NO;
    picker.modalPresentationStyle = UIModalPresentationOverFullScreen;

    [[Util getCurrentVC] presentViewController:picker animated:YES completion:nil];
}

// TZImagePickerController 进行 单或多个 视频、图片的选择
- (void)selectMulti {
    TZImagePickerController *imagePicker = [[TZImagePickerController alloc] initWithMaxImagesCount:maxSelectNum columnNumber:5 delegate:self];
    imagePicker.allowPickingOriginalPhoto = NO;
    imagePicker.allowPickingMultipleVideo = allowVideo;
    imagePicker.allowPickingVideo = allowVideo;
    imagePicker.allowPickingImage = allowPhoto;
    imagePicker.allowTakePicture = NO;
    imagePicker.allowCameraLocation = NO;
    imagePicker.allowTakeVideo = NO;
    [imagePicker setPreferredLanguage:@"zh-Hans"];
    imagePicker.modalPresentationStyle = UIModalPresentationFullScreen;
    [[Util getCurrentVC] presentViewController:imagePicker animated:YES completion:nil];
}

// 选择照片的回调
// assets 为所选内容
- (void)imagePickerController:(TZImagePickerController *)picker
      didFinishPickingPhotos:(NSArray<UIImage *> *)photos
                sourceAssets:(NSArray *)assets
       isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto
                       infos:(NSArray<NSDictionary *> *)infos {
    
    //返回给flutter的结果 路径数组
    __block NSMutableArray *resPathArr = @[].mutableCopy;

    //通过assets获取文件路径的线程锁
    __block pthread_mutex_t mutex;
    pthread_mutex_init(&mutex, NULL);

    //复制文件回调的线程锁
    __block pthread_mutex_t mutex_copy;
    pthread_mutex_init(&mutex_copy, NULL);

    //选择的文件数
    __block NSUInteger num = assets.count;

    if (!num) {
        [self resFlutter:@[]];
        return;
    }

    void (^copyFileBlock)(NSString *url_str, PHAsset *phAssert) = ^(NSString *url_str, PHAsset *phAssert){
        [self copyFile:url_str cb:^(NSDictionary *copy_res) {
            pthread_mutex_lock(&mutex_copy);
            ElecMaterial *material = ElecMaterial.new;
            material.content = copy_res[@"relativePath"];
            material.absolutePath = copy_res[@"absolutePath"];
            material.md5 = copy_res[@"md5"];
            [[GlobalData shareInstance].db tempMedia_insert:material];
            [resPathArr addObject:material.absolutePath];
            num--;
            if (!num) {
                [self resFlutter:resPathArr];
            }
            pthread_mutex_unlock(&mutex_copy);
        } asset:phAssert];
    };

    for (PHAsset *asset in assets) {
        switch (asset.mediaType) {
            case PHAssetMediaTypeImage: {
                //通过asset获取图片路径
                [asset requestContentEditingInputWithOptions:nil completionHandler:^(PHContentEditingInput * _Nullable contentEditingInput, NSDictionary * _Nonnull info) {
                    pthread_mutex_lock(&mutex);
                    NSString* filePath = [contentEditingInput.fullSizeImageURL.absoluteString substringFromIndex:7];//删除absoluteString前的 file://
                    copyFileBlock(filePath, asset);
                    pthread_mutex_unlock(&mutex);
                }];
                break;
            }
            case PHAssetMediaTypeVideo: {
                //通过asset获取视频路径
                [[PHImageManager defaultManager] requestAVAssetForVideo:asset options:nil resultHandler:^(AVAsset *_Nullableasset, AVAudioMix *_NullableaudioMix,NSDictionary *_Nullable info) {
                    if(_Nullableasset) {
                        pthread_mutex_lock(&mutex);
                        AVURLAsset *urlAsset = (AVURLAsset *)_Nullableasset;
                        NSURL *url = urlAsset.URL;
                        NSString* filePath = url.path;
                        copyFileBlock(filePath, nil);
                        pthread_mutex_unlock(&mutex);
                    }
                }];
                break;
            }
            default: {
                NSLog(@"非图片/视频的媒体类型 暂不处理");
            }
        }
    }
}

// 选择单个视频的回调
// TZImagePickerController.allowPickingMultipleVideo = NO 时回调
//
// 多选时回调 - (void)imagePickerController:(TZImagePickerController *)picker
//                 didFinishPickingPhotos:(NSArray<UIImage *> *)photos
//                           sourceAssets:(NSArray *)assets
//                  isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto
- (void)imagePickerController:(TZImagePickerController *)picker
       didFinishPickingVideo:(UIImage *)coverImage
                sourceAssets:(PHAsset *)asset {
    [[PHImageManager defaultManager] requestAVAssetForVideo:asset options:nil resultHandler:^(AVAsset*_Nullableasset,AVAudioMix*_NullableaudioMix,NSDictionary*_Nullable info) {
        AVURLAsset *urlAsset = (AVURLAsset *)_Nullableasset;
        NSURL *url = urlAsset.URL;
        NSString *filePath = url.path;
        [self copyFile:filePath cb:^(NSDictionary *copy_res) {
            ElecMaterial *material = ElecMaterial.new;
            material.content = copy_res[@"relativePath"];
            material.absolutePath = copy_res[@"absolutePath"];
            material.md5 = copy_res[@"md5"];
            [[GlobalData shareInstance].db tempMedia_insert:material];
            [self resFlutter:@[material.absolutePath]];
        } asset:nil];
    }];
}

- (void)tz_imagePickerControllerDidCancel:(TZImagePickerController *)picker {
    [self resFlutter:nil];
}

- (void)copyFile:(NSString *)path cb:(copyCB)cb {
    [self copyFile:path cb:cb asset:nil];
}

- (void)copyFile:(NSString *)path cb:(copyCB)cb asset:(PHAsset *)asset{
    __block NSString *suffix = @"";
    if ([path containsString:@"."]) {
        suffix = [[path componentsSeparatedByString:@"."] lastObject];
        suffix = [NSString stringWithFormat:@".%@",suffix];
    }
    
    void (^notHEIFCopyBlock)(void) = ^(){
        NSString *uuid = [Util getUUID];
        NSString *relativePath = [NSString stringWithFormat:@"%@/%@%@", self->fileDirRelative, uuid, suffix];
        NSString *absolutePath = [NSString stringWithFormat:@"%@/%@%@", self->fileDirAbsolute, uuid, suffix];
        
        FileCopy *fc = [[FileCopy alloc] init];
        NSDictionary *res = @{@"absolutePath": @"", @"relativePath": @"", @"md5": @""};
        if ([fc copyFile:path toFilePath:absolutePath]) {
            NSString *file_md5 = [EncryptUtil getFileMD5WithPath:absolutePath];
            
            ElecMaterial *existMaterial = [self checkMD5Exists:file_md5];
            if (!existMaterial) {
                res = @{@"absolutePath": absolutePath, @"relativePath": relativePath, @"md5": file_md5};
            } else {
                NSString *existAbsolutePath = [NSHomeDirectory() stringByAppendingPathComponent:existMaterial.content];
                NSString *existRelativePath = existMaterial.content;
                res = @{@"absolutePath": existAbsolutePath, @"relativePath": existRelativePath, @"md5": existMaterial.md5};
                [self deleteFile:absolutePath];
            }
        }
        if (cb) {
            cb(res);
        }
    };
    
    void (^encodeBlock)(void) = ^(){
        if ([path containsString:@"."]) {
            suffix = @".jpg";
        }
        [asset requestContentEditingInputWithOptions:nil completionHandler:^(PHContentEditingInput * _Nullable contentEditingInput, NSDictionary * _Nonnull info) {
            if (contentEditingInput.fullSizeImageURL) {
                CIImage *ciImage = [CIImage imageWithContentsOfURL:contentEditingInput.fullSizeImageURL];
                CIContext *context = [CIContext context];
                NSData *jpgData = [context JPEGRepresentationOfImage:ciImage colorSpace:ciImage.colorSpace options:@{}];
                NSString *uuid = [Util getUUID];
                NSString *relativePath = [NSString stringWithFormat:@"%@/%@%@", self->fileDirRelative, uuid, suffix];
                NSString *absolutePath = [NSString stringWithFormat:@"%@/%@%@", self->fileDirAbsolute, uuid, suffix];
                FileCopy *fc = [[FileCopy alloc] init];
                NSDictionary *res = @{@"absolutePath": @"", @"relativePath": @"", @"md5": @""};
                if ([fc copyFile:absolutePath data:jpgData]) {
                    NSString *file_md5 = [EncryptUtil getFileMD5WithPath:absolutePath];
                    ElecMaterial *existMaterial = [self checkMD5Exists:file_md5];
                    if (!existMaterial) {
                        res = @{@"absolutePath": absolutePath, @"relativePath": relativePath, @"md5": file_md5};
                    } else {
                        NSString *existAbsolutePath = [NSHomeDirectory() stringByAppendingPathComponent:existMaterial.content];
                        NSString *existRelativePath = existMaterial.content;
                        res = @{@"absolutePath": existAbsolutePath, @"relativePath": existRelativePath, @"md5": existMaterial.md5};
                        [self deleteFile:absolutePath];
                    }
                }
                if (cb) {
                    cb(res);
                }
            }
        }];
    };
    
    if (!asset) {
        notHEIFCopyBlock();
    } else {
        __block BOOL isHEIF = NO;
        if (@available(iOS 9.0, *)) {
            NSArray *resourceList = [PHAssetResource assetResourcesForAsset:asset];
            [resourceList enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                PHAssetResource *resource = obj;
                NSString *UTI = resource.uniformTypeIdentifier;
                if ([UTI isEqualToString:@"public.heif"] || [UTI isEqualToString:@"public.heic"]) {
                    isHEIF = YES;
                    *stop = YES;
                }
            }];
        } else {
            NSString *UTI = [asset valueForKey:@"uniformTypeIdentifier"];
            isHEIF = [UTI isEqualToString:@"public.heif"] || [UTI isEqualToString:@"public.heic"];
        }
        if (isHEIF) {
            encodeBlock();
        } else {
            notHEIFCopyBlock();
        }
    }
}

- (ElecMaterial *)checkMD5Exists:(NSString *)md5 {
    NSArray *selectRes = [[GlobalData shareInstance].db material_select_md5:md5];
    if (selectRes.count) {
        return [[ElecMaterial alloc] init:selectRes[0]];
    } else {
        return nil;
    }
}

- (void)deleteFile:(NSString *)path {
    if (![Util checkDefined:path] || !path.length) return;
    NSFileManager *fm = [NSFileManager defaultManager];
    NSError *error;
    if(![fm removeItemAtPath:path error:&error]){
        NSLog(@"删除文件失败 %@",[error localizedDescription]);
    }
}

- (NSUInteger)getFileLength:(NSString *)path {
    NSURL *_u = [[NSURL alloc] initFileURLWithPath:path];
    NSData *_data = [NSData dataWithContentsOfURL:_u];
    return _data.length;
}

- (NSString *)createDirInDocuments:(NSString *)dirName {
    NSString *documentsPath = [Util getAbsolutePath:@"Documents"];
    NSString *dirDocuments = [documentsPath stringByAppendingPathComponent:dirName];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    BOOL isExist = [fileManager fileExistsAtPath:dirDocuments];
    if (!isExist) {
        NSError *error;
        BOOL isSuccess = [fileManager createDirectoryAtPath:dirDocuments withIntermediateDirectories:YES attributes:nil error:&error];
        if (!isSuccess) {
            NSLog(@"error = %@", error.debugDescription);
        }
    }

    return dirDocuments;
}

- (void)deleteAllUnusedFile {
    [self deleteAllFiles:fileDirAbsolute];
}

- (void)deleteAllUpgradeCache {
    [self deleteAllFiles:upgradeFileDir_cache];
}

- (NSString *)getNewUpgradeFileCachePath {
    NSString *uuid = [Util getUUID];
    NSString *cachePath = [upgradeFileDir_cache stringByAppendingPathComponent:uuid];
    return cachePath;
}

- (void)deleteAllFiles:(NSString *)dir {
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSDirectoryEnumerator *dirEnum = [fileManager enumeratorAtPath:dir];

    NSMutableArray *dbFiles = NSMutableArray.new;
    NSArray *allMaterials = [[GlobalData shareInstance].db material_select_all];
    for (NSDictionary *materialData in allMaterials) {
        ElecMaterial *material = [[ElecMaterial alloc] init:materialData];
        [dbFiles addObject:[NSHomeDirectory() stringByAppendingPathComponent:material.content]];
    }

    NSString *fileName ;
    while (fileName = [dirEnum nextObject]) {
        NSString *absolutePath = [dir stringByAppendingPathComponent:fileName];
        BOOL delete = ![dbFiles containsObject:absolutePath];
        if (delete) {
            [self deleteFile:absolutePath];
        }
    }
}

- (NSDictionary *)moveUpgradeCacheToLocal:(NSString *)cachePath {
    //NSURL *cacheURL = [[NSURL alloc] initFileURLWithPath:cachePath];
    //NSString *cacheFileName = cacheURL.pathExtension;

    NSFileManager *fm = [NSFileManager defaultManager];

    if (![fm fileExistsAtPath:cachePath]) return @{};

    NSString *md5 = [EncryptUtil getFileMD5WithPath:cachePath];

    // 判断本地文件中是否存在相同的MD5
    // 存在则直接返回存在文件路径
    NSString *existPath = [self checkUpgradeMd5Exist:md5];

    if (existPath) return @{@"path": existPath, @"md5": md5};

    // 不存在 复制缓存文件 以MD5命名  返回新文件路径
    NSString *resPath = [upgradeFileDir stringByAppendingPathComponent:md5];
    [self moveFile:cachePath to:resPath];
    return @{@"path": resPath, @"md5": md5};
}

- (NSString *)checkUpgradeMd5Exist:(NSString *)md5 {
    NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:upgradeFileDir];
    NSString *existFileName ;
    while (existFileName = [dirEnum nextObject]) {
        if ([existFileName rangeOfString:md5].location != NSNotFound) {
            NSString *existPath = [upgradeFileDir stringByAppendingPathComponent:existFileName];
            return existPath;
        }
    }
    return nil;
}

- (BOOL)moveFile:(NSString *)from to:(NSString *)to {
    NSFileManager *fm = [NSFileManager defaultManager];

    if (![fm fileExistsAtPath:from]) return NO;

    if ([fm fileExistsAtPath:to]) return YES;

    [fm moveItemAtPath:from toPath:to error:nil];
    return YES;
}

@end
